
#include "ProgramElem.h"


#include "Type.h"

#include "Ast.h"					
#include "ParserUtil.h"					

/****************************************************************/
extern const OpNode::OpInfo opInfo[] = {
  // print name, arity, paren_flag, fixity, arg types, out type, constraints
  //
  // Paren_flag -- opnode->print() outputs is surrounded by parenthesis if 
  // this flag is set. As set below, the expression may not print correctly
  // in some rare cases, e.g., ~(b * c) will get printed as ~b * c,
  // which actually corresponds to (~b)*c. To ensure that things get printed
  // correctly all the time, more paren_flags should be set to 1, but this
  // will lead to more clutter in printed output. Basically, what we have done
  // here is to look are expressions by type -- arithmetic, relational, 
  // boolean, bit operations, etc. Within each type, the highest priority 
  // operator is printed without paren. This will work correctly, as long
  // as the language doesn't permit mixing of different types of expressions.
  // But this assumption doesn't always hold, as in the example above. Also,
  // there is an exception to this general approach in the case of unary minus
  // and * -- since (-a)*b and -(a*b) have the same meaning, we can exclude
  // paren for * without an error.
  //
  // Codes for constraints:
  // first character:
  //    N: No additional constraint over what is given by argTypes
  //    I: all arguments must have identical type
  //    S: one of the arguments must have a type that is a supertype of
  //        of all other arguments. All other arguments require a coercion 
  //        operation to be introduced so as to convert their type to S.
  //    s: one of the arguments must have a type that is a subtype of
  //        of all other arguments. 
  //    L: all list arguments (and list output) must have same type. In 
  //        addition, all non-list arguments (and output) must have same 
  //        type as that of elements in these lists
  //    T: all tuple arguments to the function must have same type.
  //    A: (assignment). Type of second argument must be a subtype of
  //       the first argument
  //
  // second character:
  //    O: output type is the same as out type. (In the following cases,
  //        the output type need not be equal to out type, but a subtype
  //        of it.) Since a TypeTag provides complete type information only
  //        for primitive types, `O' is applicable only in this case.
  //    digit: output type is the same as that of the digit'th argument
  //       In this case, a third character may be used, the code for
  //       which is as follows:
  //         'e' denotes that the output is of type alpha, where
  //             the type of digit'th argument is list(alpha)
  //         'l' denotes that the output is of type list(alpha), where
  //             alpha is the type of the digit'th argument.
  //    S: The output type is the same as that of the argument with the
  //        most general type. (Typically used with first character 'S')
  //    s: The output type is the same as that of the argument with the
  //        least general type. (Typically used with first character 'S')
  //    P: The output type is the product of the types of all arguments
  //    p: The output type is a component of the input tuple type. The
  //        following character specifies the component. A digit k specifies
  //        that the component number as k. The character 'a' indicates that
  //        the component number is given by an integer argument to the
  //        operator. The argument number is given by the following digit.
  //        'p' can be used only in conjunction with first character 'P'.
  //    L: Output type is the same as type of list arguments. Can be used
  //        only in conjunction with first character L.
  //    e: Output type is the same as type of element of list arguments. 
  //        Can be used only in conjunction with first character L.
  //
  {OpNode::UMINUS, "-",  1, 0, OpNode::PREFIX, {Type::SIGNED}, Type::SIGNED, "N1"},
  {OpNode::PLUS, "+",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::MINUS, "-",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::MULT, "*",  2, 0, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},
  {OpNode::DIV, "/",  2, 1, OpNode::INFIX, {Type::NUMERIC, Type::NUMERIC}, Type::NUMERIC, "SS"},  
  {OpNode::MOD, "%",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "S2"},
  {OpNode::EQ, "==", 2, 0, OpNode::INFIX, {Type::PRIMITIVE, Type::PRIMITIVE}, Type::BOOL, "SO"},
  {OpNode::NE, "!=", 2, 0, OpNode::INFIX, {Type::PRIMITIVE, Type::PRIMITIVE}, Type::BOOL, "SO"},
  {OpNode::GT, ">",  2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::LT, "<",  2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::GE, ">=", 2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::LE, "<=", 2, 0, OpNode::INFIX, {Type::SCALAR, Type::SCALAR}, Type::BOOL, "SO"},
  {OpNode::AND, "&&",  2, 1, OpNode::INFIX, {Type::BOOL, Type::BOOL}, Type::BOOL, "NO"},
  {OpNode::OR, "||",  2, 1, OpNode::INFIX, {Type::BOOL, Type::BOOL}, Type::BOOL, "NO"},
  {OpNode::NOT, "!",  1, 0, OpNode::PREFIX, {Type::BOOL}, Type::BOOL, "NO"}, 
  {OpNode::BITNOT, "~",  1, 0, OpNode::PREFIX, {Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::BITAND, "&",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "Ss"},
  {OpNode::BITOR, "|",  2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "SS"},
  {OpNode::BITXOR, "^",  2, 0, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "SS"},
  {OpNode::SHL, "<<", 2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::SHR, ">>", 2, 1, OpNode::INFIX, {Type::INTEGRAL, Type::INTEGRAL}, Type::INTEGRAL, "N1"},
  {OpNode::ASSIGN, "=",  2, 0, OpNode::INFIX, {Type::NATIVE, Type::NATIVE}, Type::VOID, "AO"},
  {OpNode::PRINT, "print", OpNode::VARIABLE, 1, OpNode::PREFIX, {Type::NATIVE}, Type::VOID, "NO"},
  {OpNode::INVALID, "invalid",            0, 0, OpNode::PREFIX, {}, Type::ERROR, "NO"}
};

OpNode::OpNode(OpCode op, ExprNode* a1, ExprNode* a2, 
			   int ln, int col, string file):
  ExprNode(ExprNode::OP_NODE, NULL, ln,col,file) {
  opCode_ = op;
  if (a1 != NULL) {
	arity_ = 1;
        
	arg_.push_back(a1);
	if (a2 != NULL) {
	  arity_++;
	  arg_.push_back(a2);
	}
  }
  //william: check types of a1 and a2
  typeCheck();
}

OpNode::OpNode(const OpNode &other):
  ExprNode(other) {
  arity_ = other.arity();
  opCode_ = other.opCode();
  for (unsigned int i=0; (i < other.arity()); i++) {
    if (other.arg_[i]) {
      arg_.push_back((other.arg_[i])->clone());
    } 
	else {
      arg_.push_back(NULL);
    }
  }
}

void 
OpNode::print(ostream& os, int indent) const {
  if (opInfo[opCode_].prtType_ == OpNode::PREFIX) {
	os << opInfo[opCode_].name_;
	if (arity_ > 0) {
	  if (opInfo[opCode_].needParen_) 
		os << '(';
	  for (unsigned i=0; i < arity_-1; i++) {
		if (arg_[i])
		  arg_[i]->print(os, indent);
	    else os << "NULL";
		os << ", ";
	  }
      if (arg_[arity_-1])
		arg_[arity_-1]->print(os, indent);
	  else os << "NULL";
	  if (opInfo[opCode_].needParen_) 
		os << ") ";
	}
  }
  else if ((opInfo[opCode_].prtType_ == OpNode::INFIX) && (arity_ == 2)) {
	if (opInfo[opCode_].needParen_) 
	  os << "(";
	if(arg_[0])
	  arg_[0]->print(os, indent);
	else os << "NULL";
	os << opInfo[opCode_].name_; 
	if(arg_[1])
	  arg_[1]->print(os, indent);
	else os << "NULL";
	if (opInfo[opCode_].needParen_) 
	  os << ")";
  }
  else internalErr("Unhandled case in OpNnode::print");
}

/****************************************************************/

/*
 * Implement them
 */

const Type* OpNode::typeCheck() {

    Type *t1 = NULL, *t2 =NULL, *tmpType;
    if (arg_[0]) {
        t1 = this->arg_[0]->type();
    }
    if (arg_[1] )
    {    
	if(arg_[1]->exprNodeType() == ExprNode::INV_NODE)	 
		t2 = new Type(((InvocationNode*)arg_[1])->symTabEntry()->type()->retType()->tag());
	else
		t2 = this->arg_[1]->type();
    }
    
    if ((t1!=NULL && t1->tag() == Type::ERROR) || (t2!=NULL && t2->tag() == Type::ERROR)) {
        this->type(new Type(Type::ERROR));
        return new Type(Type::ERROR);
    }
//    cout<<t1->fullName()<<":"<<t2->fullName()<<endl;
    if (this->arity_ == 1) {
        switch(opCode_) {
            case OpNode::UMINUS:
                if (t1->tag() != Type::INT)
                    this->coercedType(new Type(Type::INT));
                t1 = new Type(Type::INT);
                
                break;
            case OpNode::NOT:
                t1 = new Type(Type::BOOL);
                this->coercedType(t1);
                break;
            case OpNode::BITNOT:
                //same type
                break;
            default:
                break;
        }
        this->type(t1);
        return t1;
    }
    if (this->arity_ == 2) {                
        //bullet 1: % accepts two integer
        if (opCode_ == OpNode::MOD) {
            if (!t1->isSubType(Type::INT) || !t2->isSubType(Type::INT)) {
                //cout<<t1->fullName()<<":"<<t2->fullName()<<endl;
                errMsg("Error:Binary operation % between incorrect types");
                this->type(new Type(Type::ERROR));
                return type();
            }
            this->type(new Type(Type::INT));
            return type();
        }
        //cout<<t1->fullName()<<t2->fullName()<<endl;
        if (t1->tag() == t2->tag()) {
            //this->type(new Type(t1->tag()));
            if (t1->typeDesc()!=NULL)
                this->type(new Type(t1->typeDesc(), t1->tag()));
            //return type();
        }
        if (opCode_ == OpNode::ASSIGN) { //william: t1 = t2: type of rhs should be a subtype of lhs
            if (!t2->isSubType(*t1)) {
            	errMsg("Assigned expression must be a subtype of target",line(),0,file().c_str());
                this->type(new Type(Type::ERROR));
                return type();
            } else {
                arg_[1]->coercedType(t1);
            }
            this->type(new Type(Type::BOOL)); //output of assignment is boolean
            return type();
        }
        //bullet 2: find the most general type
         if (t1->tag() != t2->tag() && t1->isSubType(*t2)) {
            arg_[0]->coercedType(t2);
            //cout<<"coerce:"<<t2->fullName()<<endl;
            //this->type(t2);
        }
        if (t1->tag() != t2->tag() && t2->isSubType(*t1)) {
            arg_[1]->coercedType(t1);
            //cout<<"coerce:"<<t1->fullName()<<endl;
            //this->type(t1);
            
        }
        if (opCode_ == OpNode::EQ || opCode_ == OpNode::GT || opCode_ == OpNode::LT ||
                opCode_ == OpNode::GE || opCode_ == OpNode::LE || opCode_ == OpNode::AND ||
                opCode_ == OpNode::OR || opCode_ == OpNode::NOT) {
            //cout<<"Boollllllllllll"<<endl;
            type(new Type(Type::BOOL)); //output of assignment is boolean
        }
        
        if (type() == NULL) {
            if (t1->isSubType(*t2)) 
                type(t2);
            else
                type(t1);
        }
        return type();
    }

}

void OpNode::typePrint(ostream& os, int indent) const {
    //william: please modify
    if (arity_ == 1) {
        if(this->coercedType() != NULL) {
            if (opCode_ == OpNode::UMINUS)
                os<<"-";

            os<<"(";
            os<<this->coercedType()->fullName();
            os<<")";
        }
        arg_[0]->typePrint(os, indent); //print valueNode
        
    }
    if (arity_ == 2) {
	//if (opInfo[opCode_].needParen_)
	os << "(";
	if(arg_[0])
	  arg_[0]->typePrint(os, indent);
	else os << "NULL";
	os << opInfo[opCode_].name_;  //print operator
	if(arg_[1])
	  arg_[1]->typePrint(os, indent);
	else os << "NULL";
	//if (opInfo[opCode_].needParen_)
	os << ")";
    }
}
/****************************************************************/
AstNode::AstNode(NodeType nt, int line, int column, string file) :
	ProgramElem(NULL, line, column, file) {}
AstNode::AstNode(const AstNode& a) : ProgramElem(a) {}
/****************************************************************/
ExprNode::ExprNode(const ExprNode& en): AstNode(en) {}

ExprNode::ExprNode(ExprNodeType et, const Value* val, int line, int column, string file): AstNode(EXPR_NODE, line, column, file){
	exprType_ = et;
	val_ = val;
}

void ExprNode::print(ostream& os, int indent) const
{
	val_->print(os,indent);
}
/****************************************************************/
void ValueNode::print(ostream& os, int indent)const 
{
	ExprNode::print(os,indent);
}

const Type* ValueNode::typeCheck() {
    //william
    /*
    switch (this->type()->tag()) {
        case Type::BYTE:
            this->coercedType(new Type(Type::UINT));
            break;
        case Type::UINT:
            this->coercedType(new Type(Type::INT));
            break;
        case Type::INT:
            this->coercedType(new Type(Type::DOUBLE));
            break;
        default:
            cout<<"no coerce type for "<<this->type()->fullName()<<endl;
            break;
    }
     */
    
    return this->type();
}

void ValueNode::typePrint(ostream& os, int indent) const 
{
	//xiaohua hou
	if(this->coercedType())
	{
		os<<"(";
		os<<this->coercedType()->name();
		os<<")";
		os<<this->type()->name();
	}
	else
		os<<this->type()->name();
}
/****************************************************************/
const Type* ExprStmtNode::typeCheck()
{
	if (expr_ != NULL) { expr_->typeCheck();}
}
/****************************************************************/
RefExprNode::RefExprNode(string ext, const SymTabEntry* ste, int line, int column, string file)
:ExprNode(ExprNode::REF_EXPR_NODE, 0 , line,column,file)
{
	ext_ = ext;
	if(ste != NULL)
		sym_ = ste;
        // William
        //Type *t;
        /*
        SymTabEntry *ste2 = new SymTabEntry(*ste);
        if (ste->type()->typeDesc())
            t = new Type(ste2->type()->typeDesc(), ste2->type()->tag());
        else
            t = new Type(ste2->type()->tag());*/
        //type(ste->type());
        //this->type(ste->type());        
        
}

RefExprNode::RefExprNode(const RefExprNode& ren):ExprNode(ren){}

void RefExprNode::print(ostream& os, int indent)const
{
	os << ext_;
}

const Type* RefExprNode::typeCheck(){}

void RefExprNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	if(this->coercedType())
	{
		os<<"(";
		os<<this->coercedType()->name();
		os<<")";
	
	}
	
	//Ying, print CString cs2 = cs1;
	//os<<"RefExprNodePrint ";
	SymTabEntry *ste = stm.lookUp(ext_);
	if(ste){
		os<<ste->type()->name()<<" ";
		if(ste->type()->tag()==Type::CLASS)
			os<<ste->type()->typeDesc()->name();
	}
}
/****************************************************************/  
const Type* CompoundStmtNode::typeCheck()
{
	//aripio
	list<StmtNode*>::iterator iter;
	if(stmts_ != NULL)
	{
		//os<<endl;
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->typeCheck();
			//if((*iter)->stmtNodeKind()!= StmtNode::IF)
			//os<<";"<<endl;
		}

	}
}

void CompoundStmtNode::printWithoutBraces(ostream& os, int indent) const
{
	list<StmtNode*>::iterator iter;

	if(stmts_ != NULL)
	{
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->print(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)			
			os<<";"<<endl;
		}

	}
}

void CompoundStmtNode::print(ostream& os, int indent) const
{
	os<<"{";
	list<StmtNode*>::iterator iter;
	
	if(stmts_ != NULL)
	{
		os<<endl;
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->print(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)
			os<<";"<<endl;
		}

	}

	os<<"}";
}

void  CompoundStmtNode::typePrintWithoutBraces(ostream& os, int indent) const
{
	list<StmtNode*>::iterator iter;

	if(stmts_ != NULL)
	{
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->typePrint(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)
			os<<";"<<endl;
		}

	}
}

void  CompoundStmtNode::typePrint(ostream& os, int indent) const
{
	os<<"{";
	list<StmtNode*>::iterator iter;

	if(stmts_ != NULL)
	{
		os<<endl;
		for(iter = stmts_->begin(); iter != stmts_->end();iter++)
		{
			(*iter)->typePrint(os,indent);
			if((*iter)->stmtNodeKind()!= StmtNode::IF)
			os<<";"<<endl;
		}

	}

	os<<"}";
}
/****************************************************************/ 
InvocationNode::InvocationNode(const SymTabEntry * ste, vector<ExprNode*>* param, int line, int column, string file):ExprNode(ExprNode::INV_NODE,0, line,column, file)
{
	this->params(param);
	ste_ = ste;
	//typeCheck();	
} 

void InvocationNode::print(ostream& os, int indent) const
{
	os<<this->symTabEntry()->name()<<"(";

	vector<ExprNode*>::iterator iter;
	int i = 0;
	if(params_ != NULL)
	{
	for(iter = params_->begin(); iter != params_->end(); iter++)
	{
		(*iter)->print(os,indent);
		i++;
		if(i != params_->size())
		os<<",";
	}
	}
	os<<")";
	
}
InvocationNode::InvocationNode(const InvocationNode& in): ExprNode(in)
{
} 

const Type* InvocationNode::typeCheck()
{	//aripio
	vector<ExprNode*>::iterator iter;
	vector<const Type*>::const_iterator iter2;
	int i = 1;
	if(params_ != NULL)
	{
		if(params_->size() != this->symTabEntry()->type()->argTypes()->size())
		{
			string s = itoa(this->symTabEntry()->type()->argTypes()->size());
			s += " arguments expected for ";
			s += this->symTabEntry()->name();
			errMsg(s,line(),0,file().c_str());
			
		}
	else		
	for(iter = params_->begin(),iter2 = this->symTabEntry()->type()->argTypes()->begin(); 
		iter != params_->end(),iter2 != this->symTabEntry()->type()->argTypes()->end(); iter++,iter2++,i++)
	{
		if(!(*iter)->type()->isSubType((*iter2)->tag()) && (*iter)->type()->tag() != (*iter2)->tag())
		{
			string s = "Type mismatch for argument ";
			s += itoa(i) + " to " + this->symTabEntry()->name();
			errMsg(s,line(),0,file().c_str());
		}
		else
		if((*iter)->type()->tag() != (*iter2)->tag())
		{
			 (*iter)->coercedType((*iter2));
		}
	}
	}
}

void InvocationNode::typePrint(ostream& os, int indent) const
{
	os<<this->symTabEntry()->name()<<"(";

	vector<ExprNode*>::iterator iter;
	int i = 0;
	if(params_ != NULL)
	{
	for(iter = params_->begin(); iter != params_->end(); iter++)
	{
		(*iter)->typePrint(os,indent);
		i++;
		if(i != params_->size())
		os<<",";
	}
	}
	os<<")";
}
/****************************************************************/ 
RuleNode::RuleNode(BlockEntry* re, BasePatNode * pat, StmtNode* reaction, int line, int column, string file):AstNode(AstNode::RULE_NODE, line, column, file)
{
	rste_ = re;
	pat_ = pat;
	reaction_ = reaction;	
}

void RuleNode::print(ostream& os, int indent)const
{

	this->pat()->print(os,indent);
	os<<"-->";
	if(this->reaction() != NULL)
	{
		this->reaction()->print(os, indent);
		if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			os<<";"<<endl;
	}
	os<<";;";
}

const Type* RuleNode::typeCheck()
{
	//aripio
	this->pat()->typeCheck();
	//os<<"--> ";
	if(this->reaction() != NULL)
	{
		this->reaction()->typeCheck();
		//if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			//os<<";"<<endl;
	}
	//os<<";;";
}

void  RuleNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	this->pat()->typePrint(os,indent);
	os<<"--> ";
	if(this->reaction() != NULL)
	{
		this->reaction()->typePrint(os, indent);
		if(this->reaction()->stmtNodeKind() == StmtNode::COMPOUND)
			os<<";"<<endl;
	}
	os<<";;";
}
/****************************************************************/ 
PatNode::PatNode(int line, int column, string file): BasePatNode(BasePatNode::PRIMITIVE,line,column,file){}

PatNode::PatNode(PatNodeKind pk, BasePatNode *p1, BasePatNode* p2, int line, int column, string file): BasePatNode(pk, line,column,file)
{
	this->kind(pk);
	pat1_ = p1;
	pat2_ = p2;
        //this->typeCheck(); //william
}

bool PatNode::hasNeg() const
{
	return true;
}

bool PatNode::hasSeqOps() const
{
    //william 1.1.4
    if (this->kind() == BasePatNode::SEQ ||this->kind() == BasePatNode::STAR) {
        return true;
    }
    if (this->pat1_ != NULL && this->pat1_->hasSeqOps()) {
        return true;
    }
    if (this->pat2_ != NULL && this->pat2_->hasSeqOps()) {
        return true;
    }
    return false;
}

bool PatNode::hasAnyOrOther() const
{
	return true;
}

void PatNode::print(ostream& os, int indent)const
{
	os<<"(";
	if(this->kind() == BasePatNode::NEG)
		os<<"!";
	pat1_->print(os, indent);
	if(this->kind() == BasePatNode::STAR)
		os<<"**";
	if(pat2_ != NULL)
	{
	  switch(this->kind())
	 {
		case BasePatNode::SEQ: os<<":"; break;
		case BasePatNode::OR: os<<"\\/"; break; 
	 }
	  pat2_->print(os,indent);
	}
	os<<")";
}

const Type* PatNode::typeCheck(){
    //william: 1.1.4
    if (this->kind() == BasePatNode::NEG) {
        if (hasSeqOps()) {
            errMsg("Invalid use of pattern negation"); //see out07
        }        
    }
    
}

void PatNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	os<<"(";
	if(this->kind() == BasePatNode::NEG)
		os<<"!";
	pat1_->typePrint(os, indent);
	if(this->kind() == BasePatNode::STAR)
		os<<"**";
	if(pat2_ != NULL)
	{
	  switch(this->kind())
	 {
		case BasePatNode::SEQ: os<<":"; break;
		case BasePatNode::OR: os<<"\\/"; break;
	 }
	  pat2_->typePrint(os,indent);
	}
	os<<")";
}
/****************************************************************/ 
PrimitivePatNode::PrimitivePatNode(EventEntry* ee, vector<VariableEntry*>* params, ExprNode* c, int line, int column, string file): BasePatNode(BasePatNode::PRIMITIVE, line, column,file)
{
	ee_ = ee;
	params_ = params;
	cond_ = c;	
}


bool PrimitivePatNode::hasNeg() const
{
	return true;
}

bool PrimitivePatNode::hasSeqOps() const
{
	return true;
}

bool PrimitivePatNode::hasAnyOrOther() const
{
	return true;
}

void PrimitivePatNode::print(ostream& os, int indent)const
{
	os<<ee_->name();
	vector<const VariableEntry*>::const_iterator iter;
	int i = 0;
	if(this->event()->name() != "any")
{	os<<"(";
	if(this->params() != NULL)
{
	
	for(iter = this->params()->begin(); iter != this->params()->end(); iter++)
	{
		os<<(*iter)->type()->name()<<" ";
		if((*iter)->type()->tag() == Type::CLASS)
			os<<(*iter)->type()->typeDesc()->name()<<" ";
		os<<(*iter)->name();
		i++;
		if(i != params_->size())
		os<<",";
	}
	
}
	os<<")";
}
	if(this->cond() != NULL)
	{
		cout<<"|";
		this->cond()->print(os,indent);
	}
	
}

const Type* PrimitivePatNode::typeCheck(){}

void PrimitivePatNode::typePrint(ostream& os, int indent) const
{
	//xiaohua hou
	os<<ee_->name();
	vector<const VariableEntry*>::const_iterator iter;
	int i = 0;
	if(this->event()->name() != "any")
{	os<<"(";
	if(this->params() != NULL)
{

	for(iter = this->params()->begin(); iter != this->params()->end(); iter++)
	{
		os<<(*iter)->type()->name()<<" ";
		if((*iter)->type()->tag() == Type::CLASS)
			os<<(*iter)->type()->typeDesc()->name()<<" ";
		os<<(*iter)->name();
		i++;
		if(i != params_->size())
		os<<", ";
	}

}
	os<<")";
}
	if(this->cond() != NULL)
	{
		cout<<"|";
		this->cond()->typePrint(os,indent);
	}
}
/****************************************************************/ 
IfNode::IfNode(ExprNode* cond, StmtNode* thenStmt, StmtNode* elseStmt, int line, int column, string file):StmtNode(StmtNode::IF,line, column, file)
{
	cond_ = cond;
	then_ = thenStmt;
	else_ = elseStmt;	
        //this->typeCheck();
}

void IfNode::print(ostream& os, int indent) const
{
	os<<"if(";
	cond_->print(os,indent);
	os<<") ";
	if(this-> thenStmt() != NULL)	
	{
		this -> thenStmt()->print(os,indent);
		if(this->thenStmt()->stmtNodeKind() == StmtNode::EXPR|| this->thenStmt()-> stmtNodeKind() == StmtNode::COMPOUND)		
		os<<";"<<endl;
		
	}
	if(this->elseStmt() != NULL)
	{	
		os<<"else ";
		this->elseStmt()->print(os,indent);
		if(this->elseStmt()->stmtNodeKind() == StmtNode::EXPR || this->elseStmt()-> stmtNodeKind() == StmtNode::COMPOUND)		
		os<<";"<<endl;				
	}
} 

const Type* IfNode::typeCheck() {
    //william: for if-then-else statement, condition must be boolean
    if (cond_ != NULL && cond_->type()->tag()!= Type::BOOL) {

        cout<<cond_->type()->fullName()<<cond_->type()->tag()<<Type::BOOL<<endl;
        errMsg("if-then-else statement, condition must be boolean");
        return new Type(Type::ERROR);
    }
    return new Type(Type::VOID);
}
  
void  IfNode::typePrint(ostream& os, int indent) const
{
    os<<"if(";
    cond_->typePrint(os,indent);
    os<<") ";
    if(this-> thenStmt() != NULL)
    {
            this -> thenStmt()->typePrint(os,indent);
            if(this->thenStmt()->stmtNodeKind() == StmtNode::EXPR|| this->thenStmt()-> stmtNodeKind() == StmtNode::COMPOUND)
            os<<";"<<endl;

    }
    if(this->elseStmt() != NULL)
    {
            os<<"else ";
            this->elseStmt()->typePrint(os,indent);
            if(this->elseStmt()->stmtNodeKind() == StmtNode::EXPR || this->elseStmt()-> stmtNodeKind() == StmtNode::COMPOUND)
            os<<";"<<endl;
    }

}
/****************************************************************/ 
const Type* ReturnStmtNode::typeCheck(){
    //william: the value being returned is a subtype of the function return type
    if (fun_ == NULL)
        errMsg("ReturnStmtNode: No fun_");
    if (expr_ == NULL)
        errMsg("ReturnStmtNode: No return expr");
	//cout<<fun_->name()<<endl;
    const Type *funRetType = fun_->type()->retType();
    Type *retType = expr_->type();
	//cout<<funRetType->fullName()<<endl;
    if(funRetType->tag() == Type::VOID)
	errMsg("No return value expected for a void function",line(),0,file().c_str());	
    else
    if (!retType->isSubType(*funRetType)) {
        errMsg("Return value incompatible with current function's type",line(),0,file().c_str());
        return new Type(Type::ERROR);
    }
    return new Type(Type::VOID);
}

/* OpNode 
if (arity_ == 1) {
        Type *argType = new Type(opInfo[opCode_].argType_[0]);
        Type *outType = new Type(opInfo[opCode_].outType_);
        //type check
        if (!arg_[0]->type()->isSubType(*argType)) {
            errMsg("Error Type in OpNode");
        }
        switch(opCode_) {
            case OpNode::UMINUS:
                Type *t1 = new Type(Type::INT);
                this->coercedType(t1);
                break;
        }
        this->type(outType);
        return type();
    }
    if (arity_ == 2) {
        Type *argType1 = new Type(opInfo[opCode_].argType_[0]);
        Type *argType2 = new Type(opInfo[opCode_].argType_[1]);
        Type *outType = new Type(opInfo[opCode_].outType_);

        if (arg_[0]->type() == argType1 || arg_[1]->type() == argType2) {
            this->type(outType);
            return type();
        }

        if (arg_[0]->type()!= argType1 && arg_[0]->type()->isSubType(*argType1)) {
            arg_[0]->coercedType(argType1);
        } else {
            errMsg("Error Type in 1st Arg OpNode");
            outType = new Type(Type::ERROR);
        }
        if (arg_[1]->type()!= argType2 && arg_[1]->type()->isSubType(*argType2)) {
            arg_[1]->coercedType(argType2);
        } else {
            errMsg("Error Type in 2st Arg OpNode");
            outType = new Type(Type::ERROR);
        }

        this->type(outType);
        return type();
    }

 */

